/*
 * sdmmc_stack.c
 *
 *  Created on: Oct 16, 2013
 *      Author: Ken Arok
 */


#include "sdmmc.h"
#include "sdmmc_stack.h"

#if ACCESS_USB == true
#include "udi_msc.h"
#endif /* ACCESS_USB */

#ifdef DEBUG
#include <stdio.h>
#include "print_debug.h"
#include "config_debug.h"

#if DEBUG_SDMMC_STACK
static char _buf_dbg[100];
#define SDMMC_STACK_DEBUGF(...)	do {	\
	snprintf(_buf_dbg, 100, __VA_ARGS__); \
	print_dbg((const char*)_buf_dbg);	\
}while(0);
#else
#define SDMMC_STACK_DEBUGF(...)
#endif /* DEBUG_SDMMC_STACK */
#endif /* DEBUG */


storage_status_t vSDMMC_TestUnitReady(void)
{
	card_type_t	_card_type;
	sdmmc_error_t sdmmc_err;

	sdmmc_err = sdmmc_check();
	switch(sdmmc_err) {
		case SD_MMC_OK:
			_card_type = sdmmc_get_type();
			if(_card_type & (CARD_TYPE_SD | CARD_TYPE_MMC)) {
				return STORAGE_GOOD;
			}
			/* It is not SD/MMC card. */
			SDMMC_STACK_DEBUGF("Test Unit Error: %d\n", _card_type);

			return STORAGE_NO_PRESENT;

		case SD_MMC_INIT_ONGOING:
			/* SD/MMC card is busy. */
			SDMMC_STACK_DEBUGF("Test Unit Busy.\n");
			return STORAGE_BUSY;

		case SD_MMC_ERR_NO_CARD:
			/* SD/MMC is not present. */
			SDMMC_STACK_DEBUGF("Test Unit Error.\n");
			return STORAGE_NO_PRESENT;

		default:
			/* SD/MMC is not present. */
			SDMMC_STACK_DEBUGF("Test Unit Error.\n");
			return STORAGE_FAIL;
	}
}


storage_status_t vSDMMC_ReadCapacity(uint32_t *_nb_sector)
{
	uint32_t _card_capacity;

	_card_capacity = sdmmc_get_capacity();
	if(!_card_capacity) {
		return STORAGE_FAIL;
	}

	/* Calculate number of sector. */
	*_nb_sector = (_card_capacity * 2) - 1;

	return vSDMMC_TestUnitReady();
}


bool vSDMMC_IsWriteProtected(void)
{
	return sdmmc_is_write_protected();
}


bool vSDMMC_IsRemoval(void)
{
	return false;
}


#if ACCESS_USB == true

/** \name Memory to USB interface.
 */

COMPILER_WORD_ALIGNED
uint8_t sector_buf_0[SD_MMC_BLOCK_SIZE];

COMPILER_WORD_ALIGNED
uint8_t sector_buf_1[SD_MMC_BLOCK_SIZE];


storage_status_t vSDMMC_USBRead(uint32_t _addr, uint16_t _nb_sector)
{
	bool _b_first_step = true;
	uint16_t _nb_step;
	sdmmc_error_t _err;

	_err = sdmmc_init_read_blocks(_addr, _nb_sector);
	switch (_err) {
		case SD_MMC_OK:
			break;
		case SD_MMC_ERR_NO_CARD:
			SDMMC_STACK_DEBUGF("USB read error 2.\n");
			return STORAGE_NO_PRESENT;
		default:
			SDMMC_STACK_DEBUGF("USB read error 1.\n");
			return STORAGE_FAIL;
	}
	/* Pipeline the 2 transfer in order to speed-up the performances. */
	_nb_step = _nb_sector + 1;
	while (_nb_step--) {
		if (_nb_step) { // Skip last step
			/* MCI -> RAM. */
			_err = sdmmc_start_read_blocks(((_nb_step % 2) == 0) ? sector_buf_0 : sector_buf_1, 1);
			if (SD_MMC_OK != _err) {
				SDMMC_STACK_DEBUGF("USB Start Block error: 0x%X\n", _err);
				return STORAGE_FAIL;
			}
		}
		if (!_b_first_step) { // Skip first step
			/* RAM -> USB. */
			if (!udi_msc_trans_block(true,((_nb_step % 2) == 0) ? sector_buf_1 : sector_buf_0, SD_MMC_BLOCK_SIZE, NULL)) {
				return STORAGE_FAIL;
			}
		}
		else {
			_b_first_step = false;
		}
		if (_nb_step) { // Skip last step
			_err = sdmmc_wait_end_of_read_blocks();
			if (SD_MMC_OK != _err) {
				SDMMC_STACK_DEBUGF("USB End Block error: 0x%X\n", _err);
				return STORAGE_FAIL;
			}
		}
		_b_first_step = false;
	}
	return STORAGE_GOOD;
}


storage_status_t vSDMMC_USBWrite(uint32_t _addr, uint16_t _nb_sector)
{
	bool _b_first_step = true;
	uint16_t _nb_step;
	sdmmc_error_t _err;

	_err = sdmmc_init_write_blocks(addr, _nb_sector);
	switch (_err) {
		case SD_MMC_OK:
			break;
		case SD_MMC_ERR_NO_CARD:
			SDMMC_STACK_DEBUGF("USB write error 2.\n");
			return STORAGE_NO_PRESENT;
		default:
			SDMMC_STACK_DEBUGF("USB write error 1.\n");
			return STORAGE_FAIL;
	}
	/* Pipeline the 2 transfer in order to speed-up the performances. */
	_nb_step = _nb_sector + 1;
	while (_nb_step--) {
		if (!_b_first_step) { // Skip first step
			/* RAM -> MCI. */
			_err = sdmmc_start_write_blocks(((_nb_step % 2) == 0) ? sector_buf_0 : sector_buf_1, 1);
			if (SD_MMC_OK != _err) {
				SDMMC_STACK_DEBUGF("\n\rUSB Start Block error: 0x%X\n", _err);
				return STORAGE_FAIL;
			}
		}
		if (_nb_step) { // Skip last step
			/* USB -> RAM. */
			if (!udi_msc_trans_block(false, ((_nb_step % 2) == 0) ? sector_buf_1 : sector_buf_0, SD_MMC_BLOCK_SIZE, NULL)) {
				return STORAGE_FAIL;
			}
		}
		if (!_b_first_step) { // Skip first step
			_err = sdmmc_wait_end_of_write_blocks();
			if (SD_MMC_OK != _err) {
				SDMMC_STACK_DEBUGF("USB End block error: 0x%X\n", _err);
				return STORAGE_FAIL;
			}
		}
		else {
			_b_first_step = false;
		}
	}
	return STORAGE_GOOD;
}

#endif /* ACCESS_USB */


#if ACCESS_MEM_TO_RAM == true

/** \name Memory to RAM interface.
 */

storage_status_t vSDMMC_MemoryToRAM(uint32_t _addr, void *_ram)
{
	sdmmc_error_t _err;

	_err = sdmmc_init_read_blocks(_addr, 1);
	switch (_err) {
		case SD_MMC_OK:
			break;
		case SD_MMC_ERR_NO_CARD:
			SDMMC_STACK_DEBUGF("MEM2RAM error 2.\n");
			return STORAGE_NO_PRESENT;
		default:
			SDMMC_STACK_DEBUGF("MEM2RAM error 1.\n");
			return STORAGE_FAIL;
	}

	_err = sdmmc_start_read_blocks(_ram, 1);
	if (SD_MMC_OK != _err) {
		SDMMC_STACK_DEBUGF("MEM2RAM error: 0x%X Block 0x%p\n", _err, _ram);
		return STORAGE_FAIL;
	}

	_err = sdmmc_wait_end_of_read_blocks();
	if (SD_MMC_OK != _err) {
		SDMMC_STACK_DEBUGF("MEM2RAM error: 0x%X", _err);
		return STORAGE_FAIL;
	}
	return STORAGE_GOOD;
}


storage_status_t vSDMMC_RAMToMemory(uint32_t _addr, const void *_ram)
{
	sdmmc_error_t _err;

	_err = sdmmc_init_write_blocks(_addr, 1);
	switch (_err) {
		case SD_MMC_OK:
			break;
		case SD_MMC_ERR_NO_CARD:
			SDMMC_STACK_DEBUGF("RAM2MEM error 2.\n");
			return STORAGE_NO_PRESENT;
		default:
			SDMMC_STACK_DEBUGF("RAM2MEM error 1.\n");
			return STORAGE_FAIL;
	}

	_err = sdmmc_start_write_blocks(_ram, 1);
	if (SD_MMC_OK != _err) {
		SDMMC_STACK_DEBUGF("\n\rRAM2MEM error: 0x%X Block 0x%p\n", _err, _ram);
		return STORAGE_FAIL;
	}

	_err = sdmmc_wait_end_of_write_blocks();
	if (SD_MMC_OK != _err) {
		SDMMC_STACK_DEBUGF("RAM2MEM error: 0x%X\n", _err);
		return STORAGE_FAIL;
	}
	return STORAGE_GOOD;
}

#endif /* ACCESS_MEM_TO_RAM */
